; Interupts.
;
; Part 1. define interupt handles;
; Part 2. set interupts.

global init_interupt

; Control ports of 8259A.
INT_M_CTL	equ	020h	; I/O port for interrupt controller         <Master>
INT_M_CTLMASK	equ	021h	; setting bits in this port disables ints   <Master>
INT_S_CTL	equ	0A0h	; I/O port for second interrupt controller  <Slave>
INT_S_CTLMASK	equ	0A1h	; setting bits in this port disables ints   <Slave>

; Interupts:
; internal interupt
INT_VECTOR_DIVIDE		equ	0x0
INT_VECTOR_DEBUG		equ	0x1
INT_VECTOR_NMI			equ	0x2
INT_VECTOR_BREAKPOINT		equ	0x3
INT_VECTOR_OVERFLOW		equ	0x4
INT_VECTOR_BOUNDS		equ	0x5
INT_VECTOR_INVAL_OP		equ	0x6
INT_VECTOR_COPROC_NOT		equ	0x7
INT_VECTOR_DOUBLE_FAULT		equ	0x8
INT_VECTOR_COPROC_SEG		equ	0x9
INT_VECTOR_INVAL_TSS		equ	0xA
INT_VECTOR_SEG_NOT		equ	0xB
INT_VECTOR_STACK_FAULT		equ	0xC
INT_VECTOR_PROTECTION		equ	0xD
INT_VECTOR_PAGE_FAULT		equ	0xE
INT_VECTOR_COPROC_ERR		equ	0x10
; hardware interupt
INT_VECTOR_IRQ0			equ	0x20
INT_VECTOR_IRQ8			equ	0x28
; system call
INT_VECTOR_SYS_CALL		equ	0x80

; =============== PART 1 ==================

; ------- internal interupts -------
divide_error:
	push	dword 0xFFFFFFFF; no err code
	push	dword 0		; vector_no	= 0
	jmp	exception
single_step_exception:
	push	dword 0xFFFFFFFF; no err code
	push	dword 1		; vector_no	= 1
	jmp	exception
nmi:
	push	dword 0xFFFFFFFF; no err code
	push	dword 2		; vector_no	= 2
	jmp	exception
breakpoint_exception:
	push	dword 0xFFFFFFFF; no err code
	push	dword 3		; vector_no	= 3
	jmp	exception
overflow:
	push	dword 0xFFFFFFFF; no err code
	push	dword 4		; vector_no	= 4
	jmp	exception
bounds_check:
	push	dword 0xFFFFFFFF; no err code
	push	dword 5		; vector_no	= 5
	jmp	exception
inval_opcode:
	push	dword 0xFFFFFFFF; no err code
	push	dword 6		; vector_no	= 6
	jmp	exception
copr_not_available:
	push	dword 0xFFFFFFFF; no err code
	push	dword 7		; vector_no	= 7
	jmp	exception
double_fault:
	push	dword 8		; vector_no	= 8
	jmp	exception
copr_seg_overrun:
	push	dword 0xFFFFFFFF; no err code
	push	dword 9		; vector_no	= 9
	jmp	exception
inval_tss:
	push	dword 10	; vector_no	= A
	jmp	exception
segment_not_present:
	push	dword 11	; vector_no	= B
	jmp	exception
stack_exception:
	push	dword 12	; vector_no	= C
	jmp	exception
general_protection:
	push	dword 13	; vector_no	= D
	jmp	exception

extern do_page_fault
page_fault:
	pushad
	mov	eax, cr2
	push	eax	; cr2
	mov	eax, [esp + 9 * 4]
	push	eax	; error code
	call	do_page_fault
	add	esp, 8
	popad
	add	esp, 4	; error code
	iretd
copr_error:
	push	dword 0xFFFFFFFF; no err code
	push	dword 16	; vector_no	= 10h
	jmp	exception

ex_str	db	"Interupt: %d", 0
extern write_tty
extern printf
exception:
	push	dword ex_str
	call	printf
	add	esp, 8
	jmp $

; ------- hardware interupts(master) -------
%macro	hwint_master 2
	; Save registers.
	pushad
	push	gs
	push	fs
	push	es
	push	ds

	; Shut down the current interupt.
	in	al, INT_M_CTLMASK
	or	al, 1 << (%1)
	out	INT_M_CTLMASK, al

	; Open interupt.
	mov	al, 20h		; EOI
	out	20h, al
	sti

	; Call interupt handle.
	call	%2

	; Open the current interupt.
	in	al, INT_M_CTLMASK
	and	al, ~(1 << (%1))
	out	INT_M_CTLMASK, al

	; Recover registers.
	pop	ds
	pop	es
	pop	fs
	pop	gs
	popad

	iretd
%endmacro
;++++++++++++++++

extern clock_int_handle	; in schecular.c
ALIGN	16
hwint00:		; Interrupt routine for irq 0 (the clock).
	inc	byte [gs:80]
	hwint_master	0, clock_int_handle

; switch to another process, by switching the TSS selector.
global switch_to
switch_to:
	; We closed clock interupt before, and we now switch to
	; another process, while not return back to the clock
	; interupt handle, where we will open the interupt. So
	; we should open it here.
	in	al, INT_M_CTLMASK
	and	al, ~1
	out	INT_M_CTLMASK, al

	; I do not know how to write 'jmp [esp+4]:0' in nasm,
	; so I do this by hard code:
	mov	eax, [esp + 4]
	mov	[tss_s], ax
	db	0EAh	; jmp
	dd	0h	; 0
tss_s:	dw	0h	; selector of TSS

	ret

extern keyboard_int_handle
ALIGN	16
hwint01:		; Interrupt routine for irq 1 (keyboard)
	hwint_master	1, keyboard_int_handle

ALIGN	16
hwint02:		; Interrupt routine for irq 2 (cascade!)
	hwint_master	2, 0

ALIGN	16
hwint03:		; Interrupt routine for irq 3 (second serial)
	hwint_master	3, 0

ALIGN	16
hwint04:		; Interrupt routine for irq 4 (first serial)
	hwint_master	4, 0

ALIGN	16
hwint05:		; Interrupt routine for irq 5 (XT winchester)
	hwint_master	5, 0

ALIGN	16
hwint06:		; Interrupt routine for irq 6 (floppy)
	hwint_master	6, 0

ALIGN	16
hwint07:		; Interrupt routine for irq 7 (printer)
	hwint_master	7, 0

; ------- hardware interupts(slave) -------
%macro	hwint_slave 2
	; Save registers.
	pushad
	push	gs
	push	fs
	push	es
	push	ds

	; Shut down the current interupt.
	in	al, INT_S_CTLMASK
	or	al, 1 << (%1 - 8)
	out	INT_S_CTLMASK, al

	; Open interupt.
	mov	al, 20h		; EOI to master
	out	20h, al
	mov	al, 20h		; EOI to slave
	out	0A0h, al
	sti

	; Call interupt handle.
	call	%2

	; Open the current interupt.
	in	al, INT_S_CTLMASK
	and	al, ~(1 << (%1 - 8))
	out	INT_S_CTLMASK, al

	; Recover registers.
	pop	ds
	pop	es
	pop	fs
	pop	gs
	popad

	iretd
%endmacro
;++++++++++++++++

ALIGN	16
hwint08:		; Interrupt routine for irq 8 (realtime clock).
	hwint_slave	8, 0

ALIGN	16
hwint09:		; Interrupt routine for irq 9 (irq 2 redirected)
	hwint_slave	9, 0

ALIGN	16
hwint10:		; Interrupt routine for irq 10
	hwint_slave	10, 0

ALIGN	16
hwint11:		; Interrupt routine for irq 11
	hwint_slave	11, 0

ALIGN	16
hwint12:		; Interrupt routine for irq 12
	hwint_slave	12, 0

ALIGN	16
hwint13:		; Interrupt routine for irq 13 (FPU exception)
	hwint_slave	13, 0

ALIGN	16
extern win_int_handle
hwint14:		; Interrupt routine for irq 14 (AT winchester)
	hwint_slave	14, win_int_handle

ALIGN	16
hwint15:		; Interrupt routine for irq 15
	hwint_slave	15, 0

; ------- system call interupt -------
do_sys_call:
	; This is used in fork, because it's related
	; to esp in C function.
	push	ebp

	; Arguments.
	push	edi
	push	esi
	push	edx
	push	ecx
	push	ebx
	sti

	; system call handle
	call	[SysCallVector + eax * 4]
	add	esp, 6 * 4
	iretd

extern	gettime		;0
extern	getpid
extern	getppid
extern	do_fork
extern	alarm
extern	wake_up		;5
extern	do_exec
extern	do_exit
extern	waitpid
extern	do_wait
extern	open		;10
extern	close
extern	remove
extern	stat
extern	read
extern	write		;15
extern	lseek
extern	mkdir
extern	rmdir
extern	flush
extern	opendir		;20
extern	readdir
extern	read_tty
extern	write_tty
extern	format
extern	do_exec1	;25
extern	do_debug
extern	shm_create
extern	shm_remove
extern	shm_attach
extern	shm_detach	;30

SysCallVector:
	dd	gettime		;0
	dd	getpid
	dd	getppid
	dd	do_fork
	dd	alarm
	dd	wake_up		;5
	dd	do_exec
	dd	do_exit
	dd	waitpid
	dd	do_wait

	dd	open		;10
	dd	close
	dd	remove
	dd	stat
	dd	read
	dd	write		;15
	dd	lseek
	dd	mkdir
	dd	rmdir
	dd	flush
	dd	opendir		;20
	dd	readdir

	dd	read_tty
	dd	write_tty

	dd	format
	dd	do_exec1	;25
	dd	do_debug

	dd	shm_create
	dd	shm_remove
	dd	shm_attach
	dd	shm_detach	;30

; =============== PART 2 ==================

; ------ Initialize 8259A ------
%macro out_byte 2
	mov	dx, %1
	mov	al, %2
	out	dx, al
	nop
%endmacro

init_8259A:
	out_byte	INT_M_CTL,	0x11		; Master 8259, ICW1.
	out_byte	INT_S_CTL,	0x11		; Slave  8259, ICW1.
	out_byte	INT_M_CTLMASK,	INT_VECTOR_IRQ0	; Master 8259, ICW2.
	out_byte	INT_S_CTLMASK,	INT_VECTOR_IRQ8	; Slave  8259, ICW2.
	out_byte	INT_M_CTLMASK,	0x4		; Master 8259, ICW3.
	out_byte	INT_S_CTLMASK,	0x2		; Slave  8259, ICW3.
	out_byte	INT_M_CTLMASK,	0x1		; Master 8259, ICW4.
	out_byte	INT_S_CTLMASK,	0x1		; Slave  8259, ICW4.

	out_byte	INT_M_CTLMASK,	0xF8		; Master 8259, OCW1. 
	out_byte	INT_S_CTLMASK,	0xBF		; Slave  8259, OCW1. 
	ret

; ------ Initialize IDT ------

SELECTOR_KERNEL_CS	equ	8
DA_386IGate		equ	08Eh
PRIVILEGE_KRNL		equ	0
PRIVILEGE_USER		equ	3

%macro	init_idt_desc 2
	mov	eax, %2
	mov	word [edi],   ax		; offset 1
	mov	word [edi+2], SELECTOR_KERNEL_CS; selector
	mov	byte [edi+4], 0			; deserved
	mov	byte [edi+5], DA_386IGate | (%1 << 5)	; attributes
	shr	eax, 16
	mov	word [edi+6], ax		; offset 2
	add	edi, 8
%endmacro

; Address of IDT, defined at include/layout.h
; lineal:0xC0000000 phy:0x0
ADDR_IDT		equ	0C0000000h

init_idt:
	mov	edi, ADDR_IDT
	init_idt_desc	PRIVILEGE_KRNL, divide_error
	init_idt_desc	PRIVILEGE_KRNL, single_step_exception
	init_idt_desc	PRIVILEGE_KRNL, nmi
	init_idt_desc	PRIVILEGE_USER, breakpoint_exception
	init_idt_desc	PRIVILEGE_USER, overflow
	init_idt_desc	PRIVILEGE_KRNL, bounds_check
	init_idt_desc	PRIVILEGE_KRNL, inval_opcode
	init_idt_desc	PRIVILEGE_KRNL, copr_not_available
	init_idt_desc	PRIVILEGE_KRNL, double_fault
	init_idt_desc	PRIVILEGE_KRNL, copr_seg_overrun
	init_idt_desc	PRIVILEGE_KRNL, inval_tss
	init_idt_desc	PRIVILEGE_KRNL, segment_not_present
	init_idt_desc	PRIVILEGE_KRNL, stack_exception
	init_idt_desc	PRIVILEGE_KRNL, general_protection
	init_idt_desc	PRIVILEGE_KRNL, page_fault
	init_idt_desc	PRIVILEGE_KRNL, copr_error

	mov	edi, ADDR_IDT + INT_VECTOR_IRQ0 * 8	
	init_idt_desc	PRIVILEGE_KRNL, hwint00
	init_idt_desc	PRIVILEGE_KRNL, hwint01
	init_idt_desc	PRIVILEGE_KRNL, hwint02
	init_idt_desc	PRIVILEGE_KRNL, hwint03
	init_idt_desc	PRIVILEGE_KRNL, hwint04
	init_idt_desc	PRIVILEGE_KRNL, hwint05
	init_idt_desc	PRIVILEGE_KRNL, hwint06
	init_idt_desc	PRIVILEGE_KRNL, hwint07
	init_idt_desc	PRIVILEGE_KRNL, hwint08
	init_idt_desc	PRIVILEGE_KRNL, hwint09
	init_idt_desc	PRIVILEGE_KRNL, hwint10
	init_idt_desc	PRIVILEGE_KRNL, hwint11
	init_idt_desc	PRIVILEGE_KRNL, hwint12
	init_idt_desc	PRIVILEGE_KRNL, hwint13
	init_idt_desc	PRIVILEGE_KRNL, hwint14
	init_idt_desc	PRIVILEGE_KRNL, hwint15

	mov	edi, ADDR_IDT + INT_VECTOR_SYS_CALL * 8	
	init_idt_desc	PRIVILEGE_USER, do_sys_call
	ret

idt_ptr:
	dw	256 * 8 - 1
	dd	ADDR_IDT

init_interupt:
	call init_8259A
	call init_idt

	; load idt
	cli
	lidt	[idt_ptr]
	ret
